package com.autumn.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 日期帮助
 *
 * @author 杨昌国
 *         <p>
 *         2017-09-30 15:33:57
 */
public class DateUtils {

	private final static Map<String, SimpleDateFormat> DATE_REG_EX = new HashMap<>();

	/**
	 * 日期格式化(yyyy-MM-dd)
	 */
	public final static SimpleDateFormat FORMAT_DATE = new SimpleDateFormat("yyyy-MM-dd");

	/**
	 * 日期时间格式化(yyyy-MM-dd HH:mm:ss)
	 */
	public final static SimpleDateFormat FORMAT_DATE_TIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	/**
	 * 日期时间毫秒格式化(yyyy-MM-dd HH:mm:ss.SSS)
	 */
	public final static SimpleDateFormat FORMAT_DATE_TIME_MILLISECOND = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

	/**
	 * 时间格式化HH:mm:ss)
	 */
	public final static SimpleDateFormat FORMAT_TIME = new SimpleDateFormat("HH:mm:ss");

	/**
	 * 默认时间格式化(yyyy-MM-dd HH:mm:ss)
	 */
	public final static SimpleDateFormat FORMAT_DEFAULT = FORMAT_DATE_TIME;

	static {
		DATE_REG_EX.put("[\\d]{4}-[\\d]{1,2}-[\\d]{1,2}", new SimpleDateFormat("yyyy-MM-dd"));

		DATE_REG_EX.put("[\\d]{4}-[\\d]{1,2}-[\\d]{1,2} [\\d]{1,2}:[\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

		DATE_REG_EX.put("[\\d]{4}-[\\d]{1,2}-[\\d]{1,2} [\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy-MM-dd HH:mm"));

		DATE_REG_EX.put("[\\d]{4}年[\\d]{1,2}月[\\d]{1,2}日", new SimpleDateFormat("yyyy年MM月dd日"));

		DATE_REG_EX.put("[\\d]{4}年[\\d]{1,2}月[\\d]{1,2}日 [\\d]{1,2}时[\\d]{1,2}分[\\d]{1,2}秒",
				new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"));

		DATE_REG_EX.put("[\\d]{4}年[\\d]{1,2}月[\\d]{1,2}日 [\\d]{1,2}:[\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"));

		DATE_REG_EX.put("[\\d]{4}年[\\d]{1,2}月[\\d]{1,2}日 [\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy年MM月dd日 HH:mm"));

		DATE_REG_EX.put("[\\d]{4}/[\\d]{1,2}/[\\d]{1,2}", new SimpleDateFormat("yyyy/MM/dd"));

		DATE_REG_EX.put("[\\d]{4}/[\\d]{1,2}/[\\d]{1,2} [\\d]{1,2}:[\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"));

		DATE_REG_EX.put("[\\d]{4}/[\\d]{1,2}/[\\d]{1,2} [\\d]{1,2}:[\\d]{1,2}",
				new SimpleDateFormat("yyyy/MM/dd HH:mm"));
	}

	private final static String ISO_DATE_CONTAINS = "T";

	/**
	 * 日期解析
	 *
	 * @param dateString
	 *            日期字符窜
	 * @return
	 */
	public static Date parseDate(String dateString) {
		if (StringUtils.isNullOrBlank(dateString)) {
			return null;
		}
		dateString = dateString.trim();
		for (Entry<String, SimpleDateFormat> entry : DATE_REG_EX.entrySet()) {
			if (dateString.matches(entry.getKey())) {
				try {
					return entry.getValue().parse(dateString);
				} catch (ParseException e) {
					ExceptionUtils.throwInvalidCastException("无法将字符窜 " + dateString + " 转换为 Date 类型。");
				}
			}
		}
		if (dateString.contains(ISO_DATE_CONTAINS)) {
			return parseIsoDate(dateString);
		}
		throw ExceptionUtils.throwInvalidCastException("无法将字符窜 " + dateString + " 转换为 Date 类型。");
	}

	private final static int DATE_SECTION_LENGTH = 2;

	/**
	 * Iso日期解析
	 * 
	 * @param iosDateString
	 *            日期格式
	 * @return
	 *
	 */
	public static Date parseIsoDate(String iosDateString) {
		if (iosDateString == null) {
			return null;
		}
		String[] a = iosDateString.split("T");
		String errorInfo = "无法将字符窜 " + iosDateString + " 转换为 IosDate 类型。";
		if (a.length != DATE_SECTION_LENGTH) {
			throw ExceptionUtils.throwInvalidCastException(errorInfo);
		}
		String[] b = a[1].split("\\+");
		if (b.length > DATE_SECTION_LENGTH) {
			throw ExceptionUtils.throwInvalidCastException(errorInfo);
		}
		String[] c = b[0].split("[.]");
		if (c.length > DATE_SECTION_LENGTH) {
			throw ExceptionUtils.throwInvalidCastException(errorInfo);
		}
		Date date;
		try {
			date = FORMAT_DATE_TIME.parse(a[0] + " " + c[0]);
		} catch (ParseException e) {
			throw ExceptionUtils.throwInvalidCastException("无法将字符窜 " + iosDateString + " 转换为 IosDate 类型。");
		}
		return date;
	}

	/**
	 * 在指定的日期上添加时间间隔
	 *
	 * @param date
	 *            日期
	 * @param timeSpan
	 *            时间间隔
	 * @return
	 * @author 杨昌国 2017-09-30 15:44:54
	 */
	public static Date add(Date date, TimeSpan timeSpan) {
		ExceptionUtils.checkNotNull(date, "date");
		ExceptionUtils.checkNotNull(timeSpan, "timeSpan");
		return new Date(date.getTime() + timeSpan.getTotalMilliseconds());
	}

	/**
	 * 在指定的日期上添加年份
	 *
	 * @param date
	 *            日期
	 * @param years
	 *            年数
	 * @return
	 * @author 杨昌国 2017-09-30 15:44:54
	 */
	public static Date addYear(Date date, int years) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.YEAR, years);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加月份
	 *
	 * @param date
	 * @param months
	 * @return
	 * @author 杨昌国 2017-09-30 15:46:45
	 */
	public static Date addMonth(Date date, int months) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.MONTH, months);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加天数
	 *
	 * @param date
	 *            日期
	 * @param days
	 *            天数
	 * @return
	 * @author 杨昌国 2017-09-30 15:47:44
	 */
	public static Date addDay(Date date, int days) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.DATE, days);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加小时数
	 *
	 * @param date
	 *            日期
	 * @param hours
	 *            小时数
	 * @return
	 * @author 杨昌国 2017-09-30 15:49:21
	 */
	public static Date addHour(Date date, int hours) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.HOUR_OF_DAY, hours);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加分钟数
	 *
	 * @param date
	 *            日期
	 * @param minutes
	 *            分钟
	 * @return
	 * @author 杨昌国 2017-09-30 15:50:15
	 */
	public static Date addMinute(Date date, int minutes) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.MINUTE, minutes);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加秒数
	 *
	 * @param date
	 *            日期
	 * @param seconds
	 *            秒数
	 * @return
	 * @author 杨昌国 2017-09-30 15:51:21
	 */
	public static Date addSecond(Date date, int seconds) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.SECOND, seconds);
		return calendar.getTime();
	}

	/**
	 * 在指定的日期上添加毫秒数
	 *
	 * @param date
	 *            日期
	 * @param millisecond
	 *            毫秒数
	 * @return
	 * @author 杨昌国 2017-09-30 15:51:21
	 */
	public static Date addMillisecond(Date date, int millisecond) {
		ExceptionUtils.checkNotNull(date, "date");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.MILLISECOND, millisecond);
		return calendar.getTime();
	}

	/**
	 * 获取年份
	 *
	 * @param value
	 *            值
	 * @return
	 * @author 杨昌国 2017-09-30 16:10:19
	 */
	public static int getYear(Date value) {
		ExceptionUtils.checkNotNull(value, "value");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(value);
		return calendar.get(Calendar.YEAR);
	}

	/**
	 * 获取月份(从1开始)
	 *
	 * @param value
	 *            值
	 * @return
	 * @author 杨昌国 2017-09-30 16:10:19
	 */
	public static int getMonth(Date value) {
		ExceptionUtils.checkNotNull(value, "value");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(value);
		return calendar.get(Calendar.MONTH) + 1;
	}

	/**
	 * 获取日
	 *
	 * @param value
	 *            值
	 * @return
	 * @author 杨昌国 2017-09-30 16:10:19
	 */
	public static int getDay(Date value) {
		ExceptionUtils.checkNotNull(value, "value");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(value);
		return calendar.get(Calendar.DAY_OF_MONTH);
	}

	/**
	 * 获取日期部份(去掉小时、分钟、秒钟、毫秒)
	 *
	 * @param value
	 *            值
	 * @return
	 * @author 杨昌国 2017-09-30 16:06:06
	 */
	public static Date getDate(Date value) {
		ExceptionUtils.checkNotNull(value, "value");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(value);
		Time tiem = getTime(calendar);
		return new Date(calendar.getTimeInMillis() - tiem.getTotalMilliseconds());
	}

	/**
	 * 获取时间部份
	 *
	 * @param calendar
	 * @return
	 * @author 杨昌国 2017-09-30 17:37:05
	 */
	public static Time getTime(Calendar calendar) {
		ExceptionUtils.checkNotNull(calendar, "calendar");
		return new Time(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE),
				calendar.get(Calendar.SECOND), calendar.get(Calendar.MILLISECOND));
	}

	/**
	 * 获取时间部份
	 *
	 * @param value
	 *            值
	 * @return
	 * @author 杨昌国 2017-09-30 16:16:30
	 */
	public static Time getTime(Date value) {
		ExceptionUtils.checkNotNull(value, "value");
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(value);
		return getTime(calendar);
	}

	/**
	 * 两个日期相减
	 *
	 * @param left
	 *            左
	 * @param rigth
	 *            右
	 * @return
	 * @author 杨昌国 2017-10-09 18:40:14
	 */
	public static TimeSpan subtract(Date left, Date rigth) {
		ExceptionUtils.checkNotNull(left, "left");
		ExceptionUtils.checkNotNull(rigth, "rigth");
		return new TimeSpan(left.getTime() - rigth.getTime());
	}

	/**
	 * 时间格式化（YYYY-MM-dd HH:mm:ss） 如果date参数为空则默认当前系统时间
	 *
	 * @param date
	 * @return
	 */
	public static String dateFormat(Date date) {
		return dateFormat(date, FORMAT_DEFAULT);
	}

	/**
	 * 时间格式化 如果date参数为空则默认当前系统时间
	 *
	 * @param date
	 *            日期
	 * @param formatString
	 *            格式字符窜
	 * @return
	 */
	public static String dateFormat(Date date, String formatString) {
		SimpleDateFormat simpleFormat = new SimpleDateFormat(formatString);
		return dateFormat(date, simpleFormat);
	}

	/**
	 * 时间格式化 如果date参数为空则默认当前系统时间
	 *
	 * @param date
	 *            日期
	 * @param format
	 *            格式
	 * @return
	 */
	public static String dateFormat(Date date, SimpleDateFormat format) {
		if (date == null) {
			date = new Date();
		}
		synchronized (format) {
			return format.format(date);
		}
	}

	/**
	 * 获取当前系统时间
	 * 
	 * @return
	 */
	public static Date currentSystemTime() {
		return new Date();
	}

	/**
	 * 通过时间秒毫秒数判断两个时间的间隔
	 *
	 * @param date
	 * @param secondDate
	 * @return
	 */
	public static int differentDaysByMillisecond(Date date, Date secondDate) {
		int days = (int) ((secondDate.getTime() - date.getTime()) / (1000 * 3600 * 24));
		return days;
	}

	/**
	 * 获取每个月的最大天数
	 * 
	 * @param calendar
	 *            日历
	 * @return
	 */
	public static int getMonthMaxDays(Calendar calendar) {
		return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

	/**
	 * 获取每个月的最大天数
	 * 
	 * @param date
	 *            日期
	 * @return
	 */
	public static int getMonthMaxDays(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return getMonthMaxDays(calendar);
	}

	/**
	 * 获取每个月的最大天数
	 * 
	 * @param year
	 *            年度
	 * @param month
	 *            月度
	 * @return
	 */
	public static int getMonthMaxDays(int year, int month) {
		String dateStr = String.format("%s-%s-%s", year, month, 1);
		Date date;
		try {
			date = FORMAT_DATE.parse(dateStr);
		} catch (ParseException e) {
			throw ExceptionUtils.throwFormatException("指定的年度或月度不在范围之内。");
		}
		return getMonthMaxDays(date);
	}

	/**
	 * 计算两个月相关
	 * 
	 * @param beginDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @param isCalcDay
	 *            是否计算到天
	 * @return
	 */
	public static int getMonthDiff(Date beginDate, Date endDate) {
		return getMonthDiff(beginDate, endDate, true);
	}

	/**
	 * 计算两个月相关
	 * 
	 * @param beginDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @param isCalcDay
	 *            是否计算到天
	 * @return
	 */
	public static int getMonthDiff(Date beginDate, Date endDate, boolean isCalcDay) {
		Calendar c1 = Calendar.getInstance();
		Calendar c2 = Calendar.getInstance();
		c1.setTime(beginDate);
		c2.setTime(endDate);
		int year1 = c1.get(Calendar.YEAR);
		int year2 = c2.get(Calendar.YEAR);
		int month1 = c1.get(Calendar.MONTH);
		int month2 = c2.get(Calendar.MONTH);
		int months = (year2 - year1) * 12;
		months += month2 - month1;
		if (isCalcDay) {
			int day1 = c1.get(Calendar.DAY_OF_MONTH);
			int day2 = c2.get(Calendar.DAY_OF_MONTH);
			// 是否跨日
			if (day2 < day1) {
				int maxDay1 = getMonthMaxDays(c1);
				int maxDay2 = getMonthMaxDays(c2);
				// 如果都不是月底的话
				if (!(day1 == maxDay1 && day2 == maxDay2)) {
					months--;
				}
			}
		}
		return months;
	}

}
